from google.appengine.ext import db
from django.utils.translation import ugettext_lazy as _
from utils.models import ModelWithCount, ModelWithLastModified, set_fetchers

import datetime
import operator

from Choice import Choice


class Model_C_LM(ModelWithCount, ModelWithLastModified):
    is_abstract_propertied = True


class Poll(Model_C_LM):
    #__multilingual__ = ('question', 'pub_date', 'choice_list') #True, when has all form field properties
    #can contain any name of a class attribute or method
    #this properties must not need to change in a synchronized
    #fashion with the original properties.
    #XX Make Poll available in multiple languages
    #XX Handle the case in which the translation model
    # is of the same language as the original by using lambdas
    # instead of properties that will be repeated.

    # Properties
    question = db.StringProperty(_('Question'),
        required=True) #XX make use of the fact that question is indexed
    
    pub_date = db.DateTimeProperty(_('Date Published'),
        required=True,
        auto_now_add=True)

    #added_by = db.UserProperty(_('Added By'),
    #    auto_current_user_add=True)
    #XX can this raise UserNotFoundError?!!
    #XX add more ways to relate polls with users
    #XX e.g. results by age, location, etc...
        
    last_vote = db.DateTimeProperty(_('Last Vote')) #XX better name?

    votes = db.IntegerProperty(_('Votes'), #XX should votes be sharded?
        default=0,
        required=True)
    
    choice_count = db.IntegerProperty(_('Choice Count'),
        default=0,
        required=True)

    def __unicode__(self):
        return self.question
    
    def get_absolute_url(self):
        return 'polls-vote_f', (), {'poll_id': self.key().id()}
        
    def _put_with_choices(self, choice_list):
        #XX store choice_list and give choices names (0, 1, 2, ...)
        self.choice_count = len(choice_list)
        self.put()
        for choice in choice_list: #assumes self.choices is a list of str
            choice = Choice(parent=self, choice=choice)
            key = choice.put() #XX rpc = ?
            
    def put_with_choices(self, choices):
        db.run_in_transaction(self._put_with_choices, choices)

    def ordered_choices(self):
        result = Choice.all().ancestor(self).fetch(self.choice_count)
        result.sort(key=operator.attrgetter('choice'))
        self.ordered_choices = result
        return result
    
    def _delete_with_choices(self):
        self.delete()
        for choice in self.choices:
            choice.delete() #XX rpc = ?
        
    def delete_with_choices(self):
        db.run_in_transaction(self._put_with_choices)
        
    @staticmethod
    def _vote(now, keys):
        poll, choice = db.get(keys)
        if poll is None or choice is None:
            raise ValueError

        choice.votes += 1
        poll.votes += 1
        choice.last_vote = poll.last_vote = now
        poll.put()
        choice.put()
        return poll

    @classmethod
    def vote(cls, poll_id, choice_id):
        '''Increments the vote count of a choice.'''
        path = (cls.__name__, poll_id, Choice.__name__, choice_id)
        poll_key = db.Key.from_path(*path[:2])
        choice_key = db.Key.from_path(*path)
        poll = db.run_in_transaction(cls._vote,
            datetime.datetime.now(), #XX!
            (poll_key, choice_key))
        poll.gput(count_incr=False) 

set_fetchers(Poll, (10, 30),
            {'none':        lambda: Poll.all(),
             'pub_date':    lambda: Poll.all().order('-pub_date'),
             'votes':       lambda: Poll.all().order('-votes'),
             'last_vote':   lambda: Poll.all().order('-last_vote'),
             #XX contentiousness
             # Where contentiousness can be some number gotten from
             # variance, kurtosis and the error due to a scarcity
             # of votes.    
             }
            )
                              
    
